package middlewareconf

import (
	"context"
	"fmt"
	"gitee.com/xfrm/middleware/xconfig"
	"strings"
	"time"

	"gitee.com/xfrm/middleware/xlog"
)

const (
	defaultNewCacheNamespace      = "infra.cache.new"
	defaultRedisPasswordNamespace = "infra.cache.password"
)

// CacheConfig cache & redis apollo config
type CacheConfig struct {
	Addr       string
	Namespace  string
	PoolSize   int
	Timeout    time.Duration
	UseWrapper bool
	Password   string
	Cluster    string
}

// CacheKeyParts cache sdk config key parts
type CacheKeyParts struct {
	Namespace string
	Group     string
}

// GetCacheConfig ...
func (p *MiddleConf) GetCacheConfig(ctx context.Context, namespace string, mType MiddlewareType) (*CacheConfig, error) {
	fun := "MiddleConf.GetCacheConfig-->"
	xlog.Infof(ctx, "%s get apollo config namespace:%s", fun, namespace)

	addr, ok := p.getConfigStringItemWithFallback(ctx, namespace, apolloCacheConfigKeyAddr, mType)
	if !ok {
		// 去老的cache配置中找，则认为，新namespace中无配置，统一使用老namespace
		mType = MiddlewareTypeRedis
		addr, ok = p.getConfigStringItemWithFallback(ctx, namespace, apolloCacheConfigKeyAddr, mType)
		if !ok {
			return nil, fmt.Errorf("%s no addr config found", fun)
		}
	}
	xlog.Infof(ctx, "%s got config addr:%s", fun, addr)

	poolSize, ok := p.getConfigIntItemWithFallback(ctx, namespace, apolloCacheConfigKeyPoolSize, mType)
	if !ok {
		poolSize = defaultCachePoolSize
		xlog.Infof(ctx, "%s no poolSize config found, use default:%d", fun, defaultCachePoolSize)
	} else {
		xlog.Infof(ctx, "%s got config poolSize:%d", fun, poolSize)
	}

	timeout, ok := p.getConfigIntItemWithFallback(ctx, namespace, apolloCacheConfigKeyTimeout, mType)
	if !ok {
		timeout = defaultCacheTimeoutNumSeconds
		xlog.Infof(ctx, "%s no timeout config found, use default:%v secs", fun, timeout)
	}
	xlog.Infof(ctx, "%s got config timeout:%v seconds", fun, timeout)

	useWrapper, ok := p.getConfigBoolItemWithFallback(ctx, namespace, apolloCacheConfigKeyUseWrapper, mType)
	if !ok {
		useWrapper = defaultCacheUseWrapper
		xlog.Infof(ctx, "%s no usewrapper config found, use default:%v", fun, useWrapper)
	}
	xlog.Infof(ctx, "%s got config usewrapper:%v", fun, useWrapper)

	password, ok := p.config.GetStringWithNamespace(ctx, defaultRedisPasswordNamespace, addr)
	if !ok {
		xlog.Infof(ctx, "%s no password config found use null, addr:%s", fun, addr)
	}
	return &CacheConfig{
		Addr:       addr,
		Namespace:  namespace,
		PoolSize:   poolSize,
		Timeout:    time.Duration(timeout) * time.Second,
		UseWrapper: useWrapper,
		Password:   password,
		Cluster:    FetchCodisClusterName(addr),
	}, nil
}

// ParseCacheKey ...
func (p *MiddleConf) ParseCacheKey(ctx context.Context, key string) (*CacheKeyParts, error) {
	fun := "MiddleConf.ParseCacheKey-->"
	parts := strings.Split(key, apolloConfigSep)
	numParts := len(parts)

	if numParts < 4 {
		err := fmt.Errorf("%s invalid key:%s", fun, key)
		xlog.Errorf(ctx, "%s err:%v", fun, err)
		return nil, err
	}

	return &CacheKeyParts{
		Namespace: strings.Join(parts[:numParts-3], apolloConfigSep),
		Group:     parts[numParts-3],
	}, nil

}

func getNamespaceByType(middlewareType MiddlewareType) (namespace string) {
	switch middlewareType {
	case MiddlewareTypeRedis:
		namespace = xconfig.DefaultCacheNamespace
	case MiddlewareTypeKafka, MiddlewareTypeDelay, MiddlewareTypePulsar:
		namespace = xconfig.DefaultMQNamespace
	case MiddlewareTypeNewCache:
		namespace = defaultNewCacheNamespace
	}
	return
}

func FetchCodisClusterName(addr string) string {
	if addr == "" {
		return ""
	}

	return fmt.Sprintf("codis-%s", strings.Split(addr, ".")[0])
}
